﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using TLang.Values;

namespace TLang.Tests
{
    [TestClass]
    public class experiments
    {
        private static string GetFilePath(string name)
        {
            string file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../experiments/" + name);

            return file;
        }

        [TestMethod]
        public void arrayTest()
        {
            string text = @"
-- empty array

(define a [])
a";
            Value value = Interpreter.InterpText(text);
            Assert.AreEqual("[]", value.ToString());
        }

        [TestMethod]
        public void ArraySetTest()
        {
            string text = @"
(define aa [1 2 3])
(set! aa:0 10)
(set! aa.0 10)
(set! aa#0 10)
aa";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(10, ((IntValue)value.Values[0]).Value);
        }

        [TestMethod]
        public void RecordSetTest()
        {
            string text = @"
(define bb {:x 1 :y 2})
(set! bb:x 10)
(set! bb.x 10)
(set! bb#x 10)
bb";
            RecordType value = (RecordType)Interpreter.InterpText(text);

            Assert.AreEqual(10, ((IntValue)value.properties.Lookup("x")).Value);
        }

        [TestMethod]
        public void RecordFunTest()
        {
            string text = @"
(define bb {:x 1 :y 2 :z (fun (x) x)})
(bb.z 10)";
            IntValue value = (IntValue)Interpreter.InterpText(text);

            Assert.AreEqual(10, value.Value);
        }

        [TestMethod]
        public void DefineTest1()
        {
            string text = @"
(define [x y] [1 2])
[x y]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(1, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(2, ((IntValue)value.Values[1]).Value);
        }

        [TestMethod]
        public void DefineTest2()
        {
            string text = @"
(define {:a x :b y} {:a 1 :b 2})
[x y]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(1, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(2, ((IntValue)value.Values[1]).Value);
        }

        [TestMethod]
        public void DefineTest3()
        {
            string text = @"
(define
  {
    :c xx
    :d [uu vv]
  }
  {
   :d [8 9]
   :c 7
  }
)

[xx uu vv]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(7, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(8, ((IntValue)value.Values[1]).Value);
            Assert.AreEqual(9, ((IntValue)value.Values[2]).Value);
        }

        [TestMethod]
        public void DefineTest4()
        {
            string text = @"
(define arr [1 2 3])
(define [t1 t2 t3] arr)
[t1 t2 t3]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(1, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(2, ((IntValue)value.Values[1]).Value);
            Assert.AreEqual(3, ((IntValue)value.Values[2]).Value);
        }

        [TestMethod]
        public void DefineTest5()
        {
            string text = @"
(define [u1 [v1 v2] u2] [2 [3 5] 7])
[u1 u2 v1 v2]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(2, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(7, ((IntValue)value.Values[1]).Value);
            Assert.AreEqual(3, ((IntValue)value.Values[2]).Value);
            Assert.AreEqual(5, ((IntValue)value.Values[3]).Value);
        }

        [TestMethod]
        public void SetTest()
        {
            string text = @"
(define [x y] [1 2])
(set! [x y] [y x])
[x y]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(2, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(1, ((IntValue)value.Values[1]).Value);
        }

        [TestMethod]
        public void SetTest2()
        {
            string text = @"
(define [x y] [1 2])
(set! {:a x :b y} {:a 7 :b 8})
[x y]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(7, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(8, ((IntValue)value.Values[1]).Value);
        }

        [TestMethod]
        public void IfTest()
        {
            string text = @"
(if (< 3 2) 1 2)";
            IntValue value = (IntValue)Interpreter.InterpText(text);
            Assert.AreEqual(2, value.Value);
        }

        [TestMethod]
        public void IfTest1()
        {
            string text = @"
(if (> 3 2) 1 2)";
            IntValue value = (IntValue)Interpreter.InterpText(text);
            Assert.AreEqual(1, value.Value);
        }

        [TestMethod]
        public void ScopeTest()
        {
            string text = @"
(define x 19)

(define ok (fun (x) (set! x 10) x))
[(ok 8) x]";
            Vector value = (Vector)Interpreter.InterpText(text);

            Assert.AreEqual(10, ((IntValue)value.Values[0]).Value);
            Assert.AreEqual(19, ((IntValue)value.Values[1]).Value);
        }

        [TestMethod]
        public void assign1Test()
        {
            string text = @"
(define aa [1 2 3])
(set! aa#0 10)
(define bb {:x 1 :y 2})
(set! bb.x 10)
[aa bb]


(define [x y] [1 2])
[x y]

(set! [x y] [y x])
[x y]

(set! {:a x :b y} {:a 7 :b 8})
[x y]


(define
  {
    :c xx
    :d [uu vv]
  }
  {
   :d [8 9]
   :c 7
  }
)

[xx uu vv]

(if (< 3 2) 10 (seq (define notthere 24) (+ 1 notthere)))

(seq (define x 19) x)

(define ok (fun (x) (set! x 10) x))
[(ok 8) x]

(define arr [1 2 3])
(define [t1 t2 t3] arr)
[t1 t2 t3]

(define [u1 [v1 v2] u2] [2 [3 5] 7])
[u1 u2 v1 v2]
";
            Value value = Interpreter.InterpText(text);
        }

        [TestMethod]
        public void RecordTest()
        {
            string text = @"
(record A :e 1 :f 2)";
            RecordType value = (RecordType)Interpreter.InterpText(text);
        }

        [TestMethod]
        public void assign2Test()
        {
            string text = @"
-- (define (record A :e a1 :f b1) (record A :e 10 :f 20))
-- [a1 b1]


(record A :g (Int 1) :h Float)
(A)

(define g:Int 1)";
            Value value = Interpreter.InterpText(text);
        }


        [TestMethod]
        public void attrTest()
        {
            string text = @"
(record A :x 1 :y 2)
(record B :u 12 :v 2)
(define b (B :v 20))
(define a (A :x b :y 4))
a.x.u

(record C (A B) :t 42)
(define c (C))
c";
            Value value = Interpreter.InterpText(text);
        }


        [TestMethod]
        public void attr2Test()
        {
            string text = @"
(record F [x :default (fun (x) x.a)])
(record A [a :default 42])
(define d (F))
(d.x (A))

(define x [10 20 30 40])

(define bar
  (fun ()
    (define v1 [0 1 2 3])
    (define foo (fun (i) v1#i))
    (foo 1)))

(define idx (bar))
x#idx


(define mat
    [[11 12 13 14]
     [21 22 23 24]
     [31 32 33 34]
     [41 42 43 44]]
)

mat #0 #(+ 1 1)

-- (define u 1)

-- (define setU
--  (fun (x)
--   (set! u x)))

-- (setU 10)
-- u

-- (define ^ (fun (x y) (* x y)))
-- (^ 2 3.2)

-- (define aa [1 2 3])
-- (set! aa#1 10)
-- aa#1


(define v1 [1 2 3 4 5 6])
(define v2 [1 2 3 4 5 6])

v1#(v2#1)";
            Value value = Interpreter.InterpText(text);
        }

        [TestMethod]
        public void Test2Test()
        {
            string text = "(define a 100.123456)";
            Value value = Interpreter.InterpText(text);
        }

        [TestMethod]
        public void Test3Test()
        {
            string text = "100.123456";
            Value value = Interpreter.InterpText(text);
        }

        [TestMethod]
        public void DefineFunTest()
        {
            string text = @"
(define f (fun(x) (  +      x 1     ))

    )";
            Value value = Interpreter.InterpText(text);
        }

        [TestMethod]
        public void IfTest2()
        {
            string text = @"
(define x 2)
if (< x 1) then (set! x 2) else (set! x 3) end
x
";
            IntValue value = (IntValue)Interpreter.InterpText(text);
            Assert.AreEqual(3, value.Value);
        }

        [TestMethod]
        public void TestTest()
        {
            string text = @"
((fun (x y)
  (+ x y)) 1 2)";
            IntValue value = (IntValue)Interpreter.InterpText(text);
            Assert.AreEqual(3, value.Value);
        }

        [TestMethod]
        public void test2Test()
        {
            string text = @"
(define
  {:x a :y b}
  {:x 1 :y 2})

(set! {:x a :y b}
      {:x 1 :y 2})
";
            Value value = Interpreter.InterpText(text);
        }


        [TestMethod]
        public void test4Test()
        {
            string text = "(+ 2 3)";
            Value value = Interpreter.InterpText(text);
            Assert.AreEqual(5, ((IntValue)value).Value);
        }


        [TestMethod]
        public void typesTest()
        {
            string text = @"
-- factorial
(define fact
  (fun ([:x Int])
    (if (= x 0) 1 (* x (fact (- x 1))))))

(fact 5)


------------- mutural recursive functions -------------
(define even
  (fun ([:x Int])
    (if (= x 0)
        true
        (if (= x 1)
            false
            (not (odd (- x 1)))))))

(define odd
  (fun ([:x Int])
    (if (= x 0)
        false
        (if (= x 1)
            true
            (not (even (- x 1)))))))

(odd 10)


------------ records -----------
(record A
  [:x Int 10]
  [:y Bool])

-- B inherits A's fields
(record B (A)
  [:z Int])

(B :y 20 :z 30)


(define f
  (fun ([:x (U Int Float) (+ 1 2)]
        [:y Int])
    (+ x y)))

(f :y 2)

(define 我是
  (fun ([:x Int] [:n Int])
    [""我是"" x n]))

(define 三 3)

(我是 ""猪头"" 三)";
            Value value = Interpreter.InterpText(text);
        }


        [TestMethod]
        public void types2Test()
        {
            string text = @"
-- Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy
-- nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi
-- enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
-- lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
-- dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
-- dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
-- dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
-- feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option
-- congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi
-- non habent claritatem insitam; est usus legentis in iis qui facit eorum
-- claritatem. Investigationes demonstraverunt lectores legere me lius quod ii
-- legunt saepius.


-- factorial
(define fact
  (fun (x)
    (declare (: x Int))                  -- declare x to be an integer
    (if (= x 0) 1 (* x (fact (- x 1))))))

(fact 5)


------------- mutural recursive functions -------------
(define even
  (fun (x)
    (declare (: x Int))
    (if (= x 0)
        true
        (if (= x 1)
            false
            (not (odd (- x 1)))))))

(define odd
  (fun (x)
    (declare (: x Int))
    (if (= x 0)
        false
        (if (= x 1)
            true
            (not (even (- x 1)))))))

(odd 10)


------------ records -----------
(record A
  [:x 10] y)

-- B inherits A's fields
(record B (A)
        z)

(B :y 20 :z 30)


(define f
  (fun ([:x (+ 1 2)] y)
    (declare (: x (U Int Float))
             (: y Int))
    (+ x y)))

(f :y 2)


(define f
  (fun ([:x (+ 1 2)] y)
    (declare (: x (U Int Float))
             (: y Int))
    (+ x y)))


(define f
  (fun (x y)
    (declare [x :type (U Int Float)
                :default 10]
             [y :type Int
                :default 20
                :escape true])
    (+ x y)))


(record A (B C)
        [x :type Int :default 10 :mutable true]
        [y :type Bool :default 20])";
            Value value = Interpreter.InterpText(text);
        }


        [TestMethod]
        public void types3Test()
        {
            string text = @"
-- (define f
--   (fun (x y)
--     (declare [x :type Int :default 1]
--              [y :type Bool :default true])
--     [x y]))

-- necessary to be able to refer to a var's type
-- (define f
--   (fun ([x Any] [y Bool] [-> x.type])
--     x))

(define f
  (fun ([x Int] [y Bool] [-> String])
    [x y]))

-- (define f
--   (fun ([x Int]
--         [y Bool]
--         [-> String])
--     [x y]))

-- (define f
--   (fun ([x :type Int :default 10]
--         [y :type Bool :default true])
--     [x y]))

(f :x 1 :y false)
-- (f 3 5)


-- (record A
--   [x Int :default 1]
--   [y Bool :default true])

-- (A :x 10 :y false)


(define list-of
  (fun (T)
    (record Pair
      [first T]
      [rest (list-of T)])))

(list-of Int)";
            Value value = Interpreter.InterpText(text);
        }
    }
}
